package com.ezhan.install;

import com.ezhan.api.Service.InstallServiceImpl;
import com.ezhan.entity.PackageInfo;
import com.ezhan.utils.PathUtils;

import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import static com.ezhan.logging.LogManager.log;
import static com.ezhan.message.NotifierManager.showError;
import static com.ezhan.message.NotifierManager.showSuccess;
import static com.ezhan.utils.PathUtils.replacePathPlaceholder;

public class MysqlInstaller {
    private final InstallServiceImpl installServiceImpl = new InstallServiceImpl();

    public boolean installMySQLFromZip(PackageInfo packageInfo, String installPath, File parentDir) {
        log("开始安装MySQL (ZIP模式)");

        String mysqlInstallPath = parentDir.getAbsolutePath() +
                "\\MySQL\\mysql-8.0.37-winx64\\bin\\mysql.exe";  // 保存MySQL的安装路径，以便后续导入SQL

        // 1. 解压ZIP文件
        File zipFile = new File(parentDir, packageInfo.fileName);
        String targetDir = replacePathPlaceholder(packageInfo.installArgs[0], installPath);
        String configDir = targetDir + "\\mysql-8.0.37-winx64";

        if (!unzipMySQLPackage(zipFile, targetDir)) {
            return false;
        }
        log("MySQL ZIP文件解压完成: " + targetDir);

        // 2. 创建配置文件
        if (!createMySQLConfigFile(configDir)) {
            return false;
        }

        // 3. 初始化数据目录
        if (!initializeMySQLData(configDir)) {
            return false;
        }

        // 4. 安装服务(服务如果存在删掉，重新安装）
        if (!installMySQLService(configDir)) {
            return false;
        }

        // 5. 启动服务
        if (!startMySQLService()) {
            return false;
        }

        // 6. 设置root密码
        String password = "123456"; // 默认密码
        if (!setMySQLRootPassword(configDir, password)) {
            return false;
        }

        // 7. 验证安装
        if (testMySQLConnection(configDir, password)) {
            // 8. 设置系统变量
            installServiceImpl.setEnv(packageInfo, installPath);
            // 9. 导入SQL
            importSql(mysqlInstallPath, parentDir);
            return true;
        } else {
            return false;
        }
    }

    //解压zip压缩包
    private boolean unzipMySQLPackage(File zipFile, String targetDir) {
        //tyr-with-resources,可以自动关闭资源
        try (ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFile))) {
            File destDir = new File(targetDir);
            //无文件路径对象，则创建
            if (!destDir.exists()) {
                destDir.mkdirs();
            }

            ZipEntry entry = zipIn.getNextEntry();
            while (entry != null) {
                // 处理文件名中的特殊字符
                String safeEntryName = entry.getName().replace("\t", "_");
                File file = new File(destDir, safeEntryName);
                if (entry.isDirectory()) {
                    file.mkdirs();
                } else {
                    File parent = file.getParentFile();
                    if (!parent.exists()) {
                        parent.mkdirs();
                    }

                    try (FileOutputStream fos = new FileOutputStream(file)) {
                        byte[] buffer = new byte[8192];
                        int length;
                        while ((length = zipIn.read(buffer)) > 0) {
                            fos.write(buffer, 0, length);
                        }
                    }
                }
                zipIn.closeEntry();
                entry = zipIn.getNextEntry();
            }
            return true;
        } catch (Exception e) {
            log("解压MySQL ZIP文件失败: " + e.getMessage());
            return false;
        }
    }

    //创建配置文件
    private boolean createMySQLConfigFile(String installPath) {
        try {
            PathUtils safe = new PathUtils();
            File configFile = new File(installPath, "my.ini");
            try (PrintWriter writer = new PrintWriter(configFile)) {
                writer.println("[mysqld]");
                writer.println("basedir=" + safe.safePath(installPath));
                writer.println("datadir=" + safe.safePath(installPath + "\\data"));
                writer.println("port=3306");
                writer.println("character-set-server=utf8mb4");
                writer.println("collation-server=utf8mb4_unicode_ci");
                writer.println("");
                writer.println("[client]");
                writer.println("default-character-set=utf8mb4");
            }
            log("MySQL配置文件创建成功: " + configFile.getAbsolutePath());
            return true;
        } catch (Exception e) {
            log("创建MySQL配置文件失败: " + e.getMessage());
            return false;
        }
    }

    // 初始化数据库data文件
    private boolean initializeMySQLData(String installPath) {
        String safeInstallPath = installPath;
        String safeDataPath = installPath + "\\data";
        try {
            String[] initCmd = {
                    safeInstallPath + "\\bin\\mysqld.exe",
                    "--initialize-insecure",
                    "--user=mysql",
                    "--basedir=" + safeInstallPath,
                    "--datadir=" + safeDataPath
            };

            log("初始化MySQL数据目录...");
            Process initProcess = getProcess(initCmd);

            // 读取输出
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(initProcess.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    log(line);
                }
            }

            int exitCode = initProcess.waitFor();
            if (exitCode != 0) {
                log("数据目录初始化失败，退出代码: " + exitCode);
                return false;
            }

            log("MySQL数据目录初始化成功");
            return true;
        } catch (Exception e) {
            log("数据目录初始化错误: " + e.getMessage());
            return false;
        }
    }

    // 初始化数据库服务
    private boolean installMySQLService(String installPath) {
        try {
            // 检查服务是否已存在,存在删掉
            if (isServiceInstalled("MySQL80")) {
                log("MySQL服务已存在，尝试删除...");
                if (!uninstallMySQLService()) {
                    log("警告: 无法删除现有服务");
                    return false;
                }
            }

            String safeInstallPath = installPath;
            String configFile = installPath + "\\my.ini";

            String[] installCmd = {
                    safeInstallPath + "\\bin\\mysqld.exe",
                    "--install", "MySQL80",
                    "--defaults-file=" + configFile
            };

            log("安装MySQL服务...");
            Process installProcess = getProcess(installCmd);

            // 读取输出
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(installProcess.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    log(line);
                }
            }

            int exitCode = installProcess.waitFor();
            if (exitCode != 0) {
                log("服务安装失败，退出代码: " + exitCode);
                return false;
            }

            log("MySQL服务安装成功");
            return true;
        } catch (Exception e) {
            log("服务安装错误: " + e.getMessage());
            return false;
        }
    }

    //查看mysql服务
    private boolean isServiceInstalled(String serviceName) {
        try {
            String[] cmd = {"sc", "query", serviceName};
            Process process = getProcess(cmd);
            return process.waitFor() == 0;
        } catch (Exception e) {
            return false;
        }
    }

    //卸载mysql服务
    private boolean uninstallMySQLService() {
        try {
            log("删除MySQL服务...");
            String[] uninstallCmd = {"sc", "delete", "MySQL80"};
            Process process = getProcess(uninstallCmd);
            int exitCode = process.waitFor();
            if (exitCode != 0) {
                log("服务删除失败，退出代码: " + exitCode);
                return false;
            }
            log("MySQL服务删除成功");
            return true;
        } catch (Exception e) {
            log("服务删除错误: " + e.getMessage());
            return false;
        }
    }

    //启动mysql服务
    private boolean startMySQLService() {
        try {
            log("启动MySQL服务...");
            String[] startCmd = {"net", "start", "MySQL80"};
            Process process = getProcess(startCmd);

            // 读取输出
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    log(line);
                }
            }

            int exitCode = process.waitFor();
            if (exitCode != 0) {
                log("服务启动失败，退出代码: " + exitCode);
                return false;
            }

            log("MySQL服务启动成功");
            return true;
        } catch (Exception e) {
            log("服务启动错误: " + e.getMessage());
            return false;
        }
    }

    //设置数据库密码
    private boolean setMySQLRootPassword(String installPath, String password) {
        try {
            log("设置MySQL root密码...");

            // 1. 停止服务
            if (!stopMySQLService()) {
                log("警告: 启动MySQL服务失败");
                return false;
            }

            // 2. 以安全模式启动
            String safeInstallPath = installPath;
            String[] safeCmd = {
                    safeInstallPath + "\\bin\\mysqld.exe",
                    "--console",
                    "--skip-grant-tables",
                    "--shared-memory"
            };
            log("以安全模式启动MySQL...");
            Process safeProcess = getProcess(safeCmd);

            // 等待服务启动
            Thread.sleep(10000);

            // 3. 更新密码
            log("更新root密码...");
            String[] updateCmd = {
                    installPath + "\\bin\\mysql.exe",
                    "-u", "root",
                    "-e", "FLUSH PRIVILEGES; ALTER USER 'root'@'localhost' IDENTIFIED BY '" + password + "';"
            };
            Process updateProcess = getProcess(updateCmd);
            int updateCode = updateProcess.waitFor();

            // 4. 停止安全模式
            log("停止安全模式...");
            safeProcess.destroy();
            safeProcess.waitFor(); // 等待进程结束

            // 5. 在启动服务前停止mysqld.exe
            killMySQLProcesses();

            // 6. 正常启动服务
            if (!startMySQLService()) {
                log("警告: 启动MySQL服务失败");
                return false;
            }

            if (updateCode != 0) {
                log("密码设置失败，退出代码: " + updateCode);
                return false;
            }

            log("MySQL root密码设置成功");
            return true;
        } catch (Exception e) {
            log("密码设置错误: " + e.getMessage());
            return false;
        }
    }

    //停止mysql服务
    private boolean stopMySQLService() {
        try {
            log("停止MySQL服务...");
            String[] stopCmd = {"net", "stop", "MySQL80"};
            Process process = getProcess(stopCmd);

            // 读取输出
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    log(line);
                }
            }

            int exitCode = process.waitFor();

            if (exitCode != 0) {
                log("服务停止失败，退出代码: " + exitCode);
                return false;
            }

            log("MySQL服务停止成功");
            return true;
        } catch (Exception e) {
            log("服务停止错误: " + e.getMessage());
            return false;
        }
    }

    //停止mysqld进程
    private void killMySQLProcesses() {
        try {
            log("强制终止所有mysqld.exe进程...");
            String[] killCmd = {
                    "taskkill",
                    "/F",
                    "/IM",
                    "mysqld.exe"
            };
            getProcess(killCmd);
            Thread.sleep(2000); // 等待2秒确保进程终止
        } catch (Exception e) {
            log("终止进程错误: " + e.getMessage());
        }
    }

    //测试数据库连接
    private boolean testMySQLConnection(String mysqlPath, String password) {
        String safeMysqlPath = mysqlPath + "\\bin\\mysql.exe";
        String[] testCmd = {
                safeMysqlPath,
                "-u", "root",
                "-p" + password,
                "-e", "SELECT 1;"
        };

        try {
            Process process = getProcess(testCmd);
            int exitCode = process.waitFor();
            return exitCode == 0;
        } catch (Exception e) {
            return false;
        }
    }

    //找到sql文件，启动新的线程进行导入
    private void importSql(String mysqlInstallPath, File parentDir) {
        for (File file : parentDir.listFiles()) {
            if (file.getName().endsWith(".sql")) {
                File sqlFile = file;
                log("导入的sql文件为：" + sqlFile);
                // 使用新线程执行导入，避免阻塞UI
                new Thread(() -> {
                    importSqlFile(sqlFile, mysqlInstallPath);
                }).start();
            }
        }
    }

    //命令行导入sql文件
    private void importSqlFile(File sqlFile, String mysqlInstallPath) {
        try {
            String safeMysqlPath = mysqlInstallPath;
            String safeSqlPath = sqlFile.getAbsolutePath();
            // 使用mysql命令行导入
            String[] cmd = {
                    safeMysqlPath,
                    "-u", "root",
                    "-p123456", // 使用之前设置的密码
                    "-e", "source " + safeSqlPath
            };


            Process process = getProcess(cmd);

            // 读取输出
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                log(line);
            }

            int exitCode = process.waitFor();
            if (exitCode == 0) {
                log("SQL导入成功");
                showSuccess("SQL导入成功");
            } else {
                log("SQL导入失败，退出代码: " + exitCode);
                showError("SQL导入失败");
            }
        } catch (Exception e) {
            log("导入SQL错误: " + e.getMessage());
            showError("导入SQL错误: " + e.getMessage());
        }
    }

    //使用ProcessBuilder来执行命令行
    private Process getProcess(String[] command) {
        // 记录命令
        log("执行命令: " + String.join(" ", command));

        ProcessBuilder builder = new ProcessBuilder(command);
        builder.redirectErrorStream(true);
        Process process = null;
        try {
            process = builder.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return process;
    }

}
